import { randomBytes, randomUUID as cryptoRandomUUID } from "crypto"; // Import cryptoRandomUUID
import { BaseErrorCode, McpError } from "../../types-global/errors.js"; // Corrected path

/**
 * Interface for entity prefix configuration
 */
export interface EntityPrefixConfig {
  [key: string]: string;
}

/**
 * ID Generation Options
 */
export interface IdGenerationOptions {
  length?: number;
  separator?: string;
  charset?: string;
}

/**
 * Generic ID Generator class for creating and managing unique identifiers
 */
export class IdGenerator {
  // Default charset
  private static DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  // Default separator
  private static DEFAULT_SEPARATOR = "_";
  // Default random part length
  private static DEFAULT_LENGTH = 6;
  // Entity prefixes
  private entityPrefixes: EntityPrefixConfig = {};
  // Reverse mapping for prefix to entity type lookup
  private prefixToEntityType: Record<string, string> = {};

  /**
   * Constructor that accepts entity prefix configuration
   * @param entityPrefixes Map of entity types to their prefixes
   */
  constructor(entityPrefixes: EntityPrefixConfig = {}) {
    this.setEntityPrefixes(entityPrefixes);
  }

  /**
   * Set or update entity prefixes and rebuild the reverse lookup
   * @param entityPrefixes Map of entity types to their prefixes
   */
  public setEntityPrefixes(entityPrefixes: EntityPrefixConfig): void {
    this.entityPrefixes = { ...entityPrefixes };

    // Rebuild reverse mapping
    this.prefixToEntityType = Object.entries(this.entityPrefixes).reduce(
      (acc, [type, prefix]) => {
        acc[prefix] = type;
        acc[prefix.toLowerCase()] = type;
        return acc;
      },
      {} as Record<string, string>,
    );

    // Removed logger call from setEntityPrefixes to prevent logging before initialization
  }

  /**
   * Get all registered entity prefixes
   * @returns The entity prefix configuration
   */
  public getEntityPrefixes(): EntityPrefixConfig {
    return { ...this.entityPrefixes };
  }

  /**
   * Generates a cryptographically secure random alphanumeric string
   * @param length The length of the random string to generate
   * @param charset Optional custom character set
   * @returns Random alphanumeric string
   */
  public generateRandomString(
    length: number = IdGenerator.DEFAULT_LENGTH,
    charset: string = IdGenerator.DEFAULT_CHARSET,
  ): string {
    const bytes = randomBytes(length);
    let result = "";

    for (let i = 0; i < length; i++) {
      const byte = bytes[i] ?? 0;
      result += charset[byte % charset.length];
    }

    return result;
  }

  /**
   * Generates a unique ID with an optional prefix
   * @param prefix Optional prefix to add to the ID
   * @param options Optional generation options
   * @returns A unique identifier string
   */
  public generate(prefix?: string, options: IdGenerationOptions = {}): string {
    const {
      length = IdGenerator.DEFAULT_LENGTH,
      separator = IdGenerator.DEFAULT_SEPARATOR,
      charset = IdGenerator.DEFAULT_CHARSET,
    } = options;

    const randomPart = this.generateRandomString(length, charset);

    return prefix ? `${prefix}${separator}${randomPart}` : randomPart;
  }

  /**
   * Generates a custom ID for an entity with format PREFIX_XXXXXX
   * @param entityType The type of entity to generate an ID for
   * @param options Optional generation options
   * @returns A unique identifier string (e.g., "PROJ_A6B3J0")
   * @throws {McpError} If the entity type is not registered
   */
  public generateForEntity(
    entityType: string,
    options: IdGenerationOptions = {},
  ): string {
    const prefix = this.entityPrefixes[entityType];

    if (!prefix) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Unknown entity type: ${entityType}`,
      );
    }

    return this.generate(prefix, options);
  }

  /**
   * Validates if a given ID matches the expected format for an entity type
   * @param id The ID to validate
   * @param entityType The expected entity type
   * @param options Optional validation options
   * @returns boolean indicating if the ID is valid
   */
  public isValid(
    id: string,
    entityType: string,
    options: IdGenerationOptions = {},
  ): boolean {
    const prefix = this.entityPrefixes[entityType];
    const {
      length = IdGenerator.DEFAULT_LENGTH,
      separator = IdGenerator.DEFAULT_SEPARATOR,
    } = options;

    if (!prefix) {
      return false;
    }

    const pattern = new RegExp(`^${prefix}${separator}[A-Z0-9]{${length}}$`);
    return pattern.test(id);
  }

  /**
   * Strips the prefix from an ID
   * @param id The ID to strip
   * @param separator Optional custom separator
   * @returns The ID without the prefix
   */
  public stripPrefix(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    return id.split(separator)[1] || id;
  }

  /**
   * Determines the entity type from an ID
   * @param id The ID to get the entity type for
   * @param separator Optional custom separator
   * @returns The entity type
   * @throws {McpError} If the ID format is invalid or entity type is unknown
   */
  public getEntityType(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    const parts = id.split(separator);
    if (parts.length !== 2 || !parts[0]) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Invalid ID format: ${id}. Expected format: PREFIX${separator}XXXXXX`,
      );
    }

    const prefix = parts[0];
    const entityType = this.prefixToEntityType[prefix];

    if (!entityType) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Unknown entity type prefix: ${prefix}`,
      );
    }

    return entityType;
  }

  /**
   * Normalizes an entity ID to ensure consistent uppercase format
   * @param id The ID to normalize
   * @param separator Optional custom separator
   * @returns The normalized ID in uppercase format
   */
  public normalize(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    const entityType = this.getEntityType(id, separator);
    const idParts = id.split(separator);
    const randomPart = idParts[1];

    if (!randomPart) {
      // This case should theoretically be caught by getEntityType, but this adds robustness
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Invalid ID format for normalization: ${id}. Random part is missing.`,
      );
    }

    return `${this.entityPrefixes[entityType]}${separator}${randomPart.toUpperCase()}`;
  }
}

// Create and export a default instance with an empty entity prefix configuration
export const idGenerator = new IdGenerator();

// For standalone use as a UUID generator
export const generateUUID = (): string => {
  return cryptoRandomUUID(); // Use imported cryptoRandomUUID
};
